﻿// Displayer.cpp: 实现文件
//

#include "pch.h"
#include "DcmImage.h"
#include "Displayer.h"
#include "CDicomImage.h"
#include "Utilities.h"


// Displayer

IMPLEMENT_DYNAMIC(Displayer, CWnd)

Displayer::Displayer()
	: m_pImage(nullptr)
	, m_pDib(nullptr)
	, m_action(MA_None)
{
	m_curPan = (HCURSOR)::LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_ICON_MOVE),
		IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);

	m_curWin = (HCURSOR)::LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_ICON_WIN),
		IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);

	m_curScale = (HCURSOR)::LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_ICON_SCALE),
		IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);

	m_curArrow = LoadCursor(NULL, IDC_ARROW);
}

Displayer::Displayer(CWnd* pParent/*=nullptr*/)
	: m_pImage(nullptr)
	, m_pDib(nullptr)
	, m_action(MA_None)
{
	m_curPan = (HCURSOR)::LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_ICON_MOVE),
		IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);

	m_curWin = (HCURSOR)::LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_ICON_WIN),
		IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);

	m_curScale = (HCURSOR)::LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_ICON_SCALE),
		IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);

	m_curArrow = LoadCursor(NULL, IDC_ARROW);
}

Displayer::~Displayer()
{
	DestroyCursor(m_curArrow);
	DestroyCursor(m_curPan);
	DestroyCursor(m_curWin);
	DestroyCursor(m_curScale);
}


BEGIN_MESSAGE_MAP(Displayer, CWnd)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_MOUSEWHEEL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_MBUTTONDOWN()
	ON_WM_MBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_SETCURSOR()
END_MESSAGE_MAP()



// Displayer 消息处理程序


void Displayer::OnPaint()
{
	CPaintDC dc(this); // device context for painting
					   // TODO: 在此处添加消息处理程序代码
					   // 不为绘图消息调用 CWnd::OnPaint()

	// 1. 获取客户区大小
	CRect rect;
	GetClientRect(&rect);
	int nWidth = rect.Width();
	int nHeight = rect.Height();

	// 2. 创建内存 DC 和临时位图（双缓冲载体）
	CDC memDC;
	memDC.CreateCompatibleDC(&dc); // 创建与屏幕 DC 兼容的内存 DC
	CBitmap bmp;
	bmp.CreateCompatibleBitmap(&dc, nWidth, nHeight); // 创建与客户区等大的位图
	CBitmap* pOldBmp = memDC.SelectObject(&bmp); // 将位图选入内存 DC

	memDC.FillSolidRect(rect, RGB(0, 0, 0)); 

	// 4. 使用 GDI+ 在内存 DC 上绘制图像
	Graphics gs(memDC.GetSafeHdc()); // 关联内存 DC 到 GDI+
	DrawImage(gs, m_pDib, rect, m_drawParam);

	// 5. 将内存 DC 内容一次性复制到屏幕 DC（避免闪烁）
	dc.BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);

	// 6. 清理资源
	memDC.SelectObject(pOldBmp); // 恢复原位图
}


void Displayer::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);

	// TODO: 在此处添加消息处理程序代码
	if (m_pImage) {
		FitToView();
	}
}

bool Displayer::LoadFile(const char* fn)
{
	CDicomImage* pImg = new CDicomImage();
	if (!pImg->ParseFile(fn)) {
		delete pImg;
		pImg = nullptr;
		return false;
	}

	SetImage(pImg);

	return true;
}


void Displayer::SetImage(CBaseImage* pImg)
{
	if (m_pImage) {
		delete m_pImage;
		m_pImage = nullptr;
	}

	m_pImage = pImg;

	if (m_pDib)
	{
		delete[] m_pDib;
		m_pDib = nullptr;
	}

	m_drawParam.Clear();
	m_pImage->GetDefaultWindow(m_drawParam.winCenter, m_drawParam.winWidth);

	m_pImage->CreateDIB(m_pDib, m_drawParam.width, m_drawParam.height, m_drawParam.winCenter,
		m_drawParam.winWidth, m_drawParam.nFrame, m_drawParam.bNagtive, true);

	FitToView();
}

void Displayer::PrevFrame()
{
	int nFrame = m_drawParam.nFrame - 1;
	if (nFrame < 0) {
		return;
	}

	m_drawParam.nFrame = nFrame;

	if (m_pDib)
	{
		delete[] m_pDib;
		m_pDib = nullptr;
	}

	m_pImage->CreateDIB(m_pDib, m_drawParam.width, m_drawParam.height, m_drawParam.winCenter,
		m_drawParam.winWidth, m_drawParam.nFrame, m_drawParam.bNagtive, true);

	Invalidate();
	//FitToView();
}

void Displayer::NextFrame()
{
	int nFrameCount = m_pImage->GetFrameCount();

	int nFrame = m_drawParam.nFrame + 1;
	if (nFrame > nFrameCount - 1) {
		return;
	}

	m_drawParam.nFrame = nFrame;

	if (m_pDib)
	{
		delete[] m_pDib;
		m_pDib = nullptr;
	}

	m_pImage->CreateDIB(m_pDib, m_drawParam.width, m_drawParam.height, m_drawParam.winCenter,
		m_drawParam.winWidth, m_drawParam.nFrame, m_drawParam.bNagtive, true);

	Invalidate();
	//FitToView();
}

int Displayer::GetFrameCount()
{
	if (m_pImage == nullptr)
		return 0;

	return m_pImage->GetFrameCount();
}


void Displayer::FitToView()
{
	if (m_pImage == nullptr)
		return;

	CRect rc;
	GetClientRect(&rc);

	int w = rc.Width();
	int h = rc.Height();
	int imgW = m_drawParam.width;
	int imgH = m_drawParam.height;

	float fx = w * 0.9 / imgW;
	float fy = h * 0.9 / imgH;
	float f = min(fx, fy);
	if (f < 0.02) f = 0.02;
	m_drawParam.zoom = f;
	Invalidate();
}


void Displayer::DrawImage(Gdiplus::Graphics& gs, void* pDib, CRect rcDraw, DrawParam& param)
{
	Gdiplus::Bitmap* img = nullptr;
	if (pDib)
	{
		BITMAPINFO bmi;
		ZeroMemory(&bmi, sizeof(BITMAPINFO));
		bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth = param.width;
		bmi.bmiHeader.biHeight = param.height;
		bmi.bmiHeader.biPlanes = 1;
		bmi.bmiHeader.biBitCount = 24;
		bmi.bmiHeader.biCompression = BI_RGB;
		bmi.bmiHeader.biClrImportant = 0;
		bmi.bmiHeader.biClrUsed = 0;

		img = new Gdiplus::Bitmap(&bmi, pDib);

	}

	// 图像
	if (img)
	{
		Rect rcDest;
		rcDest.X = rcDest.Y = 0;
		rcDest.Width = param.width;
		rcDest.Height = param.height;
		Matrix* mtx = param.GetMatrix(rcDraw);

		gs.SetTransform(mtx);
		gs.DrawImage(img, rcDest);
		gs.ResetTransform();

		DrawText(gs);

		delete img;
		img = nullptr;
	}
}

void Displayer::DrawText(Gdiplus::Graphics& gs)
{
	CRect rc;
	GetClientRect(&rc);

	Gdiplus::Font font(L"微软雅黑", 12);
	SolidBrush brush(Color(255, 255, 255));
	Gdiplus::PointF pt(2, 2);
	Gdiplus::RectF bound;
	int txtH = 18;

	// 左上角
	CString txt;
	txt = m_pImage->GetPatientInfo().patName;
	if (m_pImage->GetPatientInfo().sex != _T("")) {
		txt += _T(" / ") + m_pImage->GetPatientInfo().sex;
	}

	if (m_pImage->GetPatientInfo().studyAge != _T("")) {
		txt += _T(" / ") + m_pImage->GetPatientInfo().studyAge;
	}
		
	gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);

	if (m_pImage->GetStudyInfo().studyId != _T("")) {
		pt.Y += txtH;
		txt = m_pImage->GetStudyInfo().studyId;
		gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);
	}


	// 右上角
	pt.Y = 2;
	txt.Format(_T("WL: %.f WW: %.f"), m_drawParam.winCenter, m_drawParam.winWidth);
	gs.MeasureString(AtoW(txt).c_str(), -1, &font, PointF(0, 0), &bound);
	pt.X = rc.right - bound.Width + 2;
	gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);

	int nFrames = m_pImage->GetFrameCount();
	if (nFrames > 1) {
		txt.Format(_T("fr: %d/%d"), m_drawParam.nFrame + 1, nFrames);
		gs.MeasureString(AtoW(txt).c_str(), -1, &font, PointF(0, 0), &bound);
		pt.Y += txtH;
		pt.X = rc.right - bound.Width - 2;
		gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);
	}

	// 左下角
	pt.X = 2;
	pt.Y = rc.bottom - 4;

	if (m_pImage->GetStudyInfo().hospitalName != _T("")) {
		pt.Y -= txtH;
		txt = m_pImage->GetStudyInfo().hospitalName;
		gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);
	}
	
	if (m_pImage->GetSeriesInfo().seriesDate != _T("")) {
		pt.Y -= txtH;
		txt = m_pImage->GetSeriesInfo().seriesDate;
		if (m_pImage->GetSeriesInfo().seriesTime != _T("")) {
			txt += _T(" ") + m_pImage->GetSeriesInfo().seriesTime;
		}
		gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);
	}

	if (m_pImage->GetSeriesInfo().seriesDesc != _T("")) {
		pt.Y -= txtH;
		txt = m_pImage->GetSeriesInfo().seriesDesc;
		
		gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);
	}

	if (m_pImage->GetStudyInfo().modality != _T("")) {
		pt.Y -= txtH;
		txt = m_pImage->GetStudyInfo().modality;
		gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);
	}

	// 右下角
	pt.Y = rc.bottom - 4;

	txt = _T("");
	CString tmp;
	tmp = m_pImage->GetImageInfo().sliceThickness;
	if (!tmp.IsEmpty()) {
		txt = _T("T:") + tmp;
	}

	tmp = m_pImage->GetImageInfo().sliceLocation;
	if (!tmp.IsEmpty()) {
		if (!txt.IsEmpty()) txt += _T(" ");
		double location = atof(tmp);
		tmp.Format(_T("%.1f"), location);
		txt += _T("L:") + tmp;
	}

	gs.MeasureString(AtoW(txt).c_str(), -1, &font, PointF(0, 0), &bound);
	pt.Y -= txtH;
	pt.X = rc.right - bound.Width - 2;
	gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);

	txt.Format(_T("Zoom: %.2f"), m_drawParam.zoom);
	gs.MeasureString(AtoW(txt).c_str(), -1, &font, PointF(0, 0), &bound);
	pt.Y -= txtH;
	pt.X = rc.right - bound.Width - 2;
	gs.DrawString(AtoW(txt).c_str(), -1, &font, pt, &brush);
}

void Displayer::AdjustWindow(int deltaX, int deltaY)
{
	// 根据图像最大最小像素值计算调窗值，窗宽不小于1
	double minVal, maxVal;
	m_pImage->GetRange(minVal, maxVal);

	double deltaWW = (maxVal - minVal) / 1024 * deltaX;
	double deltaWC = (maxVal - minVal) / 1024 * deltaY;
	m_drawParam.winWidth += deltaWW;
	m_drawParam.winCenter += deltaWC;
	if (m_drawParam.winWidth < 1) m_drawParam.winWidth = 1;

	m_pImage->CreateDIB(m_pDib, m_drawParam.width, m_drawParam.height, m_drawParam.winCenter,
		m_drawParam.winWidth, m_drawParam.nFrame, m_drawParam.bNagtive, true);
	Invalidate();
}

void Displayer::Scale(int delta)
{
	bool reDraw = true;
	float factor = m_drawParam.zoom*0.02;
	if (delta > 0) {
		if ((m_drawParam.zoom - factor)*m_drawParam.width < 20
			|| (m_drawParam.zoom - factor)*m_drawParam.height < 20)
		{
			reDraw = false;
		}
		else {
			m_drawParam.zoom -= factor;
		}
	}
	else {
		if (m_drawParam.zoom + factor > 10) {
			reDraw = false;
		}
		else {
			m_drawParam.zoom += factor;
		}
	}

	if (reDraw)
		Invalidate();
}

void Displayer::Pan(int deltaX, int deltaY)
{
	m_drawParam.nOffsetX += deltaX;
	m_drawParam.nOffsetY += deltaY;
	Invalidate();
}

bool Displayer::Export(std::string dst, int format)
{
	if (m_pImage == nullptr) return false;
	return m_pImage->Export(dst, format);
	
}

BOOL Displayer::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (!m_pImage) return true;
	int nFrames = m_pImage->GetFrameCount();
	if (nFrames > 1) {
		if (zDelta < 0) {
			NextFrame();
		}
		else {
			PrevFrame();
		}
	}

	return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}


void Displayer::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	startWindow();
	m_lastPoint = point;
	m_hCursor = m_curWin;
	SetCursor(m_hCursor);

	CWnd::OnLButtonDown(nFlags, point);
}


void Displayer::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	endWindow();
	m_hCursor = m_curArrow;
	SetCursor(m_hCursor);

	CWnd::OnLButtonUp(nFlags, point);
}


void Displayer::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	startScale();
	m_lastPoint = point;
	m_hCursor = m_curScale;
	SetCursor(m_hCursor);

	CWnd::OnRButtonDown(nFlags, point);
}


void Displayer::OnRButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	endScale();
	m_hCursor = m_curArrow;
	SetCursor(m_hCursor);

	CWnd::OnRButtonUp(nFlags, point);
}


void Displayer::OnMButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	startPan();
	m_lastPoint = point;
	m_hCursor = m_curPan;
	SetCursor(m_hCursor);

	CWnd::OnMButtonDown(nFlags, point);
}


void Displayer::OnMButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	endPan();
	m_hCursor = m_curArrow;
	SetCursor(m_hCursor);

	CWnd::OnMButtonUp(nFlags, point);
}


void Displayer::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	int deltaX = point.x - m_lastPoint.x;
	int deltaY = point.y - m_lastPoint.y;

	CRect rc;
	GetClientRect(&rc);

	switch (m_action)
	{
	case Displayer::MA_None:
		break;
	case Displayer::MA_Window:
		AdjustWindow(deltaX, deltaY);
		break;
	case Displayer::MA_Pan:
		Pan(deltaX, deltaY);
		break;
	case Displayer::MA_Scale:
		Scale(deltaY);
		break;
	default:
		break;
	}

	m_lastPoint = point;
	CWnd::OnMouseMove(nFlags, point);
}


BOOL Displayer::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	SetCursor(m_hCursor);
	return TRUE;
	//return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
